/**
 * @author David Young
 */
package csab.tests;

import static org.junit.Assert.*;

import java.util.List;
import java.awt.Rectangle;

import org.junit.Test;

import csab.core.Collidable;
import csab.core.Sprite;
import csab.core.structs.QuadTreeNode;
import csab.tests.utility.TestSprite;

/**
 * This class is used to unit test QuadTreeNode.
 * @author David Young
 * @see csab.core.structs.QuadTreeNode
 */
public class QuadTreeNodeTest {

	/**
	 * Test method for {@link csab.core.structs.QuadTreeNode#addItem(csab.core.Collidable)}.
	 */
	@Test
	public final void testAddItem() {
		QuadTreeNode node = new QuadTreeNode(new Rectangle(1000,1000));
		Sprite item = new TestSprite();
		item.setPos(0, 0);
		item.getCollisionBox().width = 50;
		item.getCollisionBox().height = 50;
		node.addItem(item);
		
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		assertTrue("QuadTreeNode has 1 item", node.getItemCount() == 1);
		assertTrue("QuadTreeNode owns 1 item", node.getOwnedItemCount() == 1);
		assertTrue("Current level is " + 1, node.getCurrentLevel() == 1);
		
		for (int i = 0; i < QuadTreeNode.MAX_ITEMS; i++) {
			item = new TestSprite();
			item.setPos(0, 0);
			item.getCollisionBox().width = 50;
			item.getCollisionBox().height = 50;
			node.addItem(item);
		}
		
		assertTrue("QuadTreeNode has " + (QuadTreeNode.MAX_ITEMS + 1) + " items", node.getItemCount() == (QuadTreeNode.MAX_ITEMS + 1));
		assertTrue("QuadTreeNode owns 0 items", node.getOwnedItemCount() == 0);
		assertTrue("QuadTreeNode has children", node.hasChildren());

		assertTrue("Current level is " + QuadTreeNode.MAX_LEVELS, node.getCurrentLevel() == QuadTreeNode.MAX_LEVELS);
	}

	/**
	 * Test method for {@link csab.core.structs.QuadTreeNode#doCollisions(csab.core.Collidable)}.
	 */
	@Test
	public final void testDoCollision() {
		QuadTreeNode node = new QuadTreeNode(new Rectangle(1000,1000));
		
		TestSprite item = new TestSprite();
		item.setPos(0, 0);
		item.getCollisionBox().width = 50;
		item.getCollisionBox().height = 50;
		
		Sprite temp = new TestSprite();
		temp.setPos(20, 20);
		temp.getCollisionBox().width = 50;
		temp.getCollisionBox().height = 50;
		node.addItem(temp);
		
		temp = new TestSprite();
		temp.setPos(30, 30);
		temp.getCollisionBox().width = 50;
		temp.getCollisionBox().height = 50;
		node.addItem(temp);
		
		temp = new TestSprite();
		temp.setPos(40, 40);
		temp.getCollisionBox().width = 50;
		temp.getCollisionBox().height = 50;
		node.addItem(temp);
		
		temp = new TestSprite();
		temp.setPos(100, 100);
		temp.getCollisionBox().width = 50;
		temp.getCollisionBox().height = 50;
		node.addItem(temp);
		
		node.doCollisions(item);
		
		assertTrue("Item has undergone 3 collisions", item.getCollisionCount() == 3);
	}
	
	/**
	 * Test method for {@link csab.core.structs.QuadTreeNode#isColliding(csab.core.Collidable)}.
	 */
	@Test
	public final void testIsColliding() {
		QuadTreeNode node = new QuadTreeNode(new Rectangle(1000,1000));
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		Sprite item = new TestSprite();
		item.setPos(0, 0);
		item.getCollisionBox().width = 50;
		item.getCollisionBox().height = 50;
		node.addItem(item);
		
		Sprite item2 = new TestSprite();
		item2.setPos(0, 0);
		item2.getCollisionBox().width = 50;
		item2.getCollisionBox().height = 50;
		node.addItem(item2);
		
		assertTrue("Item is colliding with an item in the tree", node.isColliding(item));
		
		node.removeItem(item2);
		assertTrue("Item is not colliding with an item in the tree", !node.isColliding(item));
	}
	
	/**
	 * Test method for {@link csab.core.structs.QuadTreeNode#QuadTreeNode(java.awt.Rectangle)}.
	 */
	@Test
	public final void testQuadTreeNode() {
		QuadTreeNode node = new QuadTreeNode(new Rectangle(1000,1000));
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		assertTrue("QuadTreeNode has no items", node.getItemCount() == 0);
		assertTrue("QuadTreeNode section is (1000, 1000)", node.getSection().width == 1000 &&
														   node.getSection().height == 1000);
	}
	
	/**
	 * Test method for {@link csab.core.structs.QuadTreeNode#queryCollisions(csab.core.Collidable)}.
	 */
	@Test
	public final void testQueryCollisions() {
		QuadTreeNode node = new QuadTreeNode(new Rectangle(1000,1000));
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		Sprite item = new TestSprite();
		item.setPos(0, 0);
		item.getCollisionBox().width = 50;
		item.getCollisionBox().height = 50;
		node.addItem(item);
		
		//Collides
		Sprite item2 = new TestSprite();
		item2.setPos(0, 0);
		item2.getCollisionBox().width = 50;
		item2.getCollisionBox().height = 50;
		node.addItem(item2);
		
		item2 = new TestSprite();
		item2.setPos(25, 25);
		item2.getCollisionBox().width = 50;
		item2.getCollisionBox().height = 50;
		node.addItem(item2);	
		
		Sprite item3 = new TestSprite();
		item3.setPos(49, 49);
		item3.getCollisionBox().width = 50;
		item3.getCollisionBox().height = 50;
		node.addItem(item3);	
		
		//Does not collide
		item2 = new TestSprite();
		item2.setPos(490, 490);
		item2.getCollisionBox().width = 50;
		item2.getCollisionBox().height = 50;
		node.addItem(item2);
		
		List<Collidable> collidedwith = node.queryCollisions(item);
		assertTrue("Item has collided with 3 items.", collidedwith.size() == 3);
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		assertTrue("QuadTreeNode has 5 items", node.getItemCount() == 5);
		assertTrue("QuadTreeNode owns 5 items", node.getOwnedItemCount() == 5);
		assertTrue("Current level is " + 1, node.getCurrentLevel() == 1);
		
		node.removeItem(item2);
		collidedwith = node.queryCollisions(item);
		assertTrue("Item has collided with 3 items.", collidedwith.size() == 3);
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		assertTrue("QuadTreeNode has 4 items", node.getItemCount() == 4);
		assertTrue("QuadTreeNode owns 4 items", node.getOwnedItemCount() == 4);
		assertTrue("Current level is " + 1, node.getCurrentLevel() == 1);
		
		node.removeItem(item3);
		collidedwith = node.queryCollisions(item);
		assertTrue("Item has collided with 2 items.", collidedwith.size() == 2);
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		assertTrue("QuadTreeNode has 3 items", node.getItemCount() == 3);
		assertTrue("QuadTreeNode owns 3 items", node.getOwnedItemCount() == 3);
		assertTrue("Current level is " + 1, node.getCurrentLevel() == 1);
		
		//Does not collide with
		item3 = new TestSprite();
		item3.setPos(480, 480);
		item3.getCollisionBox().width = 50;
		item3.getCollisionBox().height = 50;
		node.addItem(item3);	
		
		//Collides with
		item3 = new TestSprite();
		item3.setPos(20, 20);
		item3.getCollisionBox().width = 50;
		item3.getCollisionBox().height = 50;
		node.addItem(item3);
		
		for(int i = 0; i < QuadTreeNode.MAX_ITEMS - 4; i++)
		{
			//Does not collide with
			item3 = new TestSprite();
			item3.setPos(700, 700);
			item3.getCollisionBox().width = 50;
			item3.getCollisionBox().height = 50;
			node.addItem(item3);
		}
		
		collidedwith = node.queryCollisions(item);
		assertTrue("Item has collided with 3 items.", collidedwith.size() == 3);
		assertTrue("QuadTreeNode has children", node.hasChildren());
		assertTrue("QuadTreeNode has 6 items", node.getItemCount() == 6);
		assertTrue("QuadTreeNode owns 1 items", node.getOwnedItemCount() == 1);
		
		assertTrue("Current level is " + 2, node.getCurrentLevel() == 2);
	}
	
	/**
	 * Test method for {@link csab.core.structs.QuadTreeNode#removeItem(csab.core.Collidable)}.
	 */
	@Test
	public final void testRemoveItem()
	{
		QuadTreeNode node = new QuadTreeNode(new Rectangle(1000, 1000));
		Sprite[] items = new TestSprite[QuadTreeNode.MAX_ITEMS + 2];
		Sprite item = new TestSprite();
		item.setPos(0, 0);
		item.getCollisionBox().width = 50;
		item.getCollisionBox().height = 50;
		node.addItem(item);
		items[0] = item;
		
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		assertTrue("QuadTreeNode has 1 item", node.getItemCount() == 1);
		assertTrue("QuadTreeNode owns 1 item", node.getOwnedItemCount() == 1);
		assertTrue("Current level is " + 1, node.getCurrentLevel() == 1);
		
		assertTrue("Item was removed from QuadTreeNode", node.removeItem(item));
		assertTrue("QuadTreeNode has no children", !node.hasChildren());
		assertTrue("QuadTreeNode has 0 items", node.getItemCount() == 0);
		assertTrue("QuadTreeNode owns 0 items", node.getOwnedItemCount() == 0);
		assertTrue("Current level is " + 1, node.getCurrentLevel() == 1);
		
		for(int i = 1; i < items.length; i++)
		{
			item = new TestSprite();
			item.setPos(0, 0);
			item.getCollisionBox().width = 50;
			item.getCollisionBox().height = 50;
			node.addItem(item);
			items[i] = item; 
		}
		
		assertTrue("QuadTreeNode has children", node.hasChildren());
		assertTrue("QuadTreeNode has " + (QuadTreeNode.MAX_ITEMS + 1) + "items", node.getItemCount() == (QuadTreeNode.MAX_ITEMS + 1));
		assertTrue("QuadTreeNode owns 0 items", node.getOwnedItemCount() == 0);

		assertTrue("Current level is " + QuadTreeNode.MAX_LEVELS, node.getCurrentLevel() == QuadTreeNode.MAX_LEVELS);
		
		for(int j = 0; j < items.length - QuadTreeNode.MIN_SUBITEMS + 1; j++)
		{
			node.removeItem(items[j]);
		}
		
		assertTrue("QuadTreeNode does have children", !node.hasChildren());
		assertTrue("QuadTreeNode has " + (QuadTreeNode.MIN_SUBITEMS - 1) + " items", node.getItemCount() == (QuadTreeNode.MIN_SUBITEMS - 1));
		assertTrue("QuadTreeNode owns " + (QuadTreeNode.MIN_SUBITEMS - 1) + " items", node.getOwnedItemCount() == (QuadTreeNode.MIN_SUBITEMS - 1));
		assertTrue("Current level is " + 1, node.getCurrentLevel() == 1);
	}
}
